﻿using Constants = LightCAD.MathLib.Constants;

namespace LightCAD.Model
{
    public enum SnapFilterType 
    {
        None = 0,
        Edge = 1,
        Mesh = 2,
        EdgeAndMesh=3,
        OnlyPlane=4
    }
    public class SnapPoint3dResult
    {
        public object SnapObj { get; set; }
        public Vector3 SnapPoint { get; set; }
        public Raycaster.Intersection Intersection { get; set; }
    }

    public class Snap3dRuntime
    {
        public Group SnapGroup = null;
        private Doc3dEditRuntime modelRt;
        private Raycaster raycaster;
      
        public Vector3 GetCameraDir() 
        {
            return raycaster.ray.Direction.Clone();
        }
        public Camera GetCamera() => raycaster.camera;

        public Snap3dRuntime(Doc3dEditRuntime modelRt)
        {
            this.modelRt = modelRt;
            this.modelRt.Doc3dEditControl.AttachEvents(OnInit, null, OnMouseEvent, null, null, null, null);
          
        }
        private void OnInit()
        {
            this.SnapGroup = this.modelRt.GetExtGroup("捕捉组");
            this.SnapGroup.renderOrder = int.MaxValue;
            this.raycaster = new Raycaster() { };
            this.raycaster._params.Line["threshold"] = 50;
        }
        private SnapFilterType snapFilterType = SnapFilterType.None;
        public void StartSnaping(SnapFilterType snapType)
        {
            this.snapFilterType = snapType;
            if (snapType == SnapFilterType.None)
            {
                EndSnaping();
                return;
            }
            this.snapPointSprite = new Sprite(new SpriteMaterial { color = new Color(0x00FFFF) ,depthFunc=Constants.AlwaysDepth});
            this.SnapGroup.add(this.snapPointSprite);
        }
        public void EndSnaping()
        {
            this.snapFilterType = SnapFilterType.None;
            this.snapPointSprite = null;
            this.SnapPointResult = null;
            this.SnapGroup.clear();
        }
        private void DoSnapPoint()
        {
            double zoom = 0;
            var intersections = this.modelRt.RayCaster(raycaster, (camera) =>
            {
                zoom = 10 / camera.zoom;
                this.raycaster._params.Line["threshold"] = zoom;

            });
            intersections.RemoveAll(i => ignorEles.Any(e => i.target.ext["Element"] == e));
            bool containEdge=(snapFilterType & SnapFilterType.Edge)== SnapFilterType.Edge;
            var intersection = containEdge? intersections.FirstOrDefault(inter => inter.target is Line):null;
            if (intersection == null)
            {
                if ((snapFilterType & SnapFilterType.Mesh) == SnapFilterType.Mesh)
                    intersection = intersections.FirstOrDefault(inter => inter.target is Mesh);
            }
            Vector3 snapedPoint = null;
            double dist = double.MaxValue;
            if (intersection != null)
            {
                snapedPoint = intersection.point;
                dist = intersection.distance;
            }
            Plane interPlane = null;
            if (intersection == null)
                for (int i = 0; i < snapPlanes.Length; i++)
                {
                    var interPoint = raycaster.ray.IntersectPlane(snapPlanes[i]);
                    if (interPoint == null)
                        continue;
                    var curDist = interPoint.DistanceTo(raycaster.ray.Origin);
                    if (curDist < dist)
                    {
                        dist = curDist;
                        interPlane = snapPlanes[i];
                        snapedPoint = interPoint;
                    }
                }

            if (interPlane==null)
            {
                if (intersection == null)
                {
                    this.SnapPointResult = null;
                    this.snapPointSprite.visible = false;
                    return; 
                }
                double thresholdSQ = zoom * zoom;
                var ele = intersection.target.ext["Element"] as IElement3d;
                if(ele.Snap3dPoints?.Length>0)
                for (int i = 0; i < ele.Snap3dPoints.Length; i++)
                {
                    var snapabelPoint = ele.Snap3dPoints[i];
                    var distSq = snapabelPoint.DistanceToSquared(intersection.point);
                    if (distSq < thresholdSQ)
                    {
                        snapedPoint = snapabelPoint;
                        thresholdSQ = distSq;
                    }
                }
            }
            this.snapPointSprite.scale.Set(zoom, zoom, 1);
            this.snapPointSprite.position.Copy(snapedPoint);
            this.snapPointSprite.visible = true;
            this. SnapPointResult = new SnapPoint3dResult();
            if (interPlane != null)
                this.SnapPointResult.SnapObj = interPlane;
            else
            {
                this.SnapPointResult.SnapObj = intersection.target; 
                this.SnapPointResult.Intersection = intersection;
            }
            this.SnapPointResult.SnapPoint = this.snapPointSprite.position;
            this.modelRt.Doc3dEditControl.RefreshControl();
        }
        public void OnMouseEvent(string name, MouseEventRuntime mouseEvent) 
        {
            if (name == "Move") 
                OnMouseMove(mouseEvent);
            else if(name=="Up")
                OnMouseUp(mouseEvent);
        }
        private Three.Sprite snapPointSprite = null;
        public SnapPoint3dResult SnapPointResult { get; private set; }
        public Vector3 SnapPoint => SnapPointResult?.SnapPoint;
        private void OnMouseMove(MouseEventRuntime mouseEvent) 
        {
            if (this.snapFilterType!=SnapFilterType.None) 
            {
                DoSnapPoint();
            }
        }
        private void OnMouseUp(MouseEventRuntime mouseEvent)
        {
        }
        private ListEx<IElement3d> ignorEles=new ListEx<IElement3d>(); 
        public void AddIgnorEles(ListEx<IElement3d> eles) 
        {
            this.ignorEles.PushNoRepeat(eles.ToArray());
        }
        public void ClearIgnorEles(ListEx<IElement3d> eles) 
        {
            for (int i = 0; i < eles.Length; i++)
            {
                this.ignorEles.Remove(eles[i]);
            }
        }
        private ListEx<Plane> snapPlanes = new ListEx<Plane>();
        public void  AddSnapPlanes(ListEx<Plane> planes) 
        {
            this.snapPlanes.AddRange(planes);
        }
        public void ClearSnapPlanes(ListEx<Plane> planes=null) 
        {
            if (planes == null)
            {
                this.snapPlanes.Clear();
                return;
            }
            for (int i = 0; i < planes.Length; i++)
            {
                this.snapPlanes.Remove(planes[i]);
            }
           
        }
    }
}
